package io.litchi.auth.Infrastructure.config;


import io.jsonwebtoken.security.Keys;
import io.litchi.auth.Infrastructure.constant.AuthConstant;
import io.litchi.auth.Infrastructure.handler.*;
import io.litchi.auth.application.service.AuthService;
import io.litchi.starter.jjwt.JwtManager;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
import org.springframework.http.MediaType;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.authentication.configuration.AuthenticationConfiguration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.AbstractHttpConfigurer;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.oidc.OidcScopes;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.oauth2.server.authorization.client.InMemoryRegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers.OAuth2AuthorizationServerConfigurer;
import org.springframework.security.oauth2.server.authorization.settings.AuthorizationServerSettings;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.resource.web.BearerTokenResolver;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint;
import org.springframework.security.web.util.matcher.MediaTypeRequestMatcher;

import javax.crypto.SecretKey;
import java.nio.charset.StandardCharsets;

import java.util.UUID;

/**
 * description: oauth2配置 来自官方文档
 *
 * @author helang
 * @since 2025-09-10
 */
@Slf4j
@Configuration
@EnableWebSecurity
@RequiredArgsConstructor
public class SecurityConfig {


    private final AuthConfig authConfig;

    private final JwtManager jwtManager;

    private final AuthService authService;

    private final LitchiJwtAuthenticationConverter litchiJwtAuthenticationConverter;


    @Value("${litchi.jjwt.secret-key}")
    private String secretKey;

    /**
     * 第一个过滤链 专门处理所有与 OAuth2/OIDC 协议相关的端点请求。暂时用不上
     *
     * @param http httpSecurity
     * @return 返回过滤链处理器
     * @throws Exception ex
     */
    @Bean
    @Order(1)
    public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http)
            throws Exception {
        OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
                OAuth2AuthorizationServerConfigurer.authorizationServer();

        http
                .securityMatcher(authorizationServerConfigurer.getEndpointsMatcher())
                .with(authorizationServerConfigurer, (authorizationServer) ->
                        authorizationServer
                                .oidc(Customizer.withDefaults())    // Enable OpenID Connect 1.0
                )
                .authorizeHttpRequests((authorize) ->
                        authorize
                                .anyRequest().authenticated()
                )
                // Redirect to the login page when not authenticated from the
                // authorization endpoint 默认认证地址 以及返回数据格式
                .exceptionHandling((exceptions) -> exceptions
                        .defaultAuthenticationEntryPointFor(
                                new LoginUrlAuthenticationEntryPoint("/login"),
                                new MediaTypeRequestMatcher(MediaType.TEXT_HTML)
                        )
                );

        return http.build();
    }

    /**
     * 添加自定义过滤器
     *
     * @param http securityHttp
     * @return 执行器
     * @throws Exception ex
     */
    @Bean
    @Order(2)
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {

        http.csrf(AbstractHttpConfigurer::disable) // 前后端分离项目通常关闭 CSRF
                .sessionManagement(session ->
                        session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers(authConfig.getLoginUri())
                        .permitAll()//放开登录接口 允许所有来源 这里使用表单登录 这个配置未使用
                        .requestMatchers(AuthConstant.NO_AUTH_PATH)
                        .permitAll()
                        .anyRequest().authenticated()
                )
                .userDetailsService(litchiUserDetailService()) //使用自定义登录处理
                .formLogin(form -> //登录处理 强制表单登录
                {
                    form.loginProcessingUrl(authConfig.getLoginUri());
                    form.successHandler(litchiLoginSuccessHandler());
                    form.failureHandler(litchiLoginFailureHandler());
                })
                .logout(logout -> logout  //退出处理
                        .logoutUrl(authConfig.getLogoutUri())
                        .logoutSuccessHandler(litchiLogoutSuccessHandler())
                        .permitAll()
                )
                .oauth2ResourceServer(oauth2 -> oauth2
                        .bearerTokenResolver(bearerTokenResolver())
                        .jwt(jwt -> jwt.decoder(jwtDecoder()).jwtAuthenticationConverter(litchiJwtAuthenticationConverter)) // 使用 decoder
                )
                .exceptionHandling(except -> except
                        .accessDeniedHandler(litchiAccessDeniedHandler())//使用自定义403处理
                        .authenticationEntryPoint(litchiAuthenticationEntryPoint()));//使用自定义401处理
        return http.build();
    }


    /**
     * 加密器
     *
     * @return 返回加密器
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    public static void main(String[] args) {
        System.out.println(new BCryptPasswordEncoder().encode("ikun666"));
    }

    /**
     * 处理oauth认证
     *
     * @return 返回xx
     */
    @Bean
    public RegisteredClientRepository registeredClientRepository() {
        RegisteredClient oidcClient = RegisteredClient.withId(UUID.randomUUID().toString())
                .clientId("oidc-client")
                .clientSecret("{noop}secret")
                .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
                .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
                .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN)
                .redirectUri("http://127.0.0.1:8080/login/oauth2/code/oidc-client")
                .postLogoutRedirectUri("http://127.0.0.1:8080/")
                .scope(OidcScopes.OPENID)
                .scope(OidcScopes.PROFILE)
                .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
                .build();

        return new InMemoryRegisteredClientRepository(oidcClient);
    }

    /**
     * 自动颁发 token 暂不使用 使用自定义jwtManager方便操作
     *
     * @return JWKSource
     */
//    @Bean
//    public JWKSource<SecurityContext> jwkSource() {
//        KeyPair keyPair = generateRsaKey();
//        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
//        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
//        RSAKey rsaKey = new RSAKey.Builder(publicKey)
//                .privateKey(privateKey)
//                .keyID(UUID.randomUUID().toString())
//                .build();
//        JWKSet jwkSet = new JWKSet(rsaKey);
//        return new ImmutableJWKSet<>(jwkSet);
//    }
//    private static KeyPair generateRsaKey() {
//        KeyPair keyPair;
//        try {
//            KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance("RSA");
//            keyPairGenerator.initialize(2048);
//            keyPair = keyPairGenerator.generateKeyPair();
//        } catch (Exception ex) {
//            throw new IllegalStateException(ex);
//        }
//        return keyPair;
//    }

    /**
     * 自定义 Bearer Token 解析器
     *
     * @return 返回token解析器
     */
    @Bean
    public BearerTokenResolver bearerTokenResolver() {
        LitchiBearerTokenResolver resolver = new LitchiBearerTokenResolver(authConfig);

        // 包装：如果路径是 放行路径，返回 null，不解析 token
        return request -> {
            String uri = request.getRequestURI();
            if (uri.startsWith(AuthConstant.NO_AUTH_PATH.replace("**", ""))) {
                return null;
            }
            return resolver.resolve(request);
        };

    }

    /**
     * jwt解析
     *
     * @return 返回解析器
     */
    @Bean
    public JwtDecoder jwtDecoder() {
        SecretKey key = Keys.hmacShaKeyFor(secretKey.getBytes(StandardCharsets.UTF_8));
        return NimbusJwtDecoder.withSecretKey(key).build();
    }

    @Bean
    public AuthorizationServerSettings authorizationServerSettings() {
        return AuthorizationServerSettings.builder().build();
    }

    // 权限管理器
    @Bean
    public AuthenticationManager authenticationManager(
            AuthenticationConfiguration config) throws Exception {
        return config.getAuthenticationManager();
    }


    //登录处理
    @Bean
    public LitchiUserDetailService litchiUserDetailService() {
        return new LitchiUserDetailService(authService);
    }

    //登录成功处理
    @Bean
    public LitchiLoginSuccessHandler litchiLoginSuccessHandler() {
        return new LitchiLoginSuccessHandler(jwtManager, authConfig, authService);
    }

    //登录失败处理
    @Bean
    public LitchiLoginFailureHandler litchiLoginFailureHandler() {
        return new LitchiLoginFailureHandler();
    }

    //退出处理
    @Bean
    public LitchiLogoutSuccessHandler litchiLogoutSuccessHandler() {
        return new LitchiLogoutSuccessHandler();
    }

    //无权限处理
    @Bean
    public LitchiAccessDeniedHandler litchiAccessDeniedHandler() {
        return new LitchiAccessDeniedHandler();
    }

    //未登录处理
    @Bean
    public LitchiAuthenticationEntryPoint litchiAuthenticationEntryPoint() {
        return new LitchiAuthenticationEntryPoint();
    }
}